/**
 * FFMpeg编码音视频过程:
 *  1.获取编码器 avcodec_find_encoder_by_name(codec_name)
 *  2.初始化编码器上下文 avcodec_alloc_context3(pCodec)
 *  3.设置编码器上下文参数
 *  4.打开编码器上下文 avcodec_open2(pCodecCtx, pCodec, ppOptions)
 *  5.初始化AVPacket，供编码使用 av_packet_alloc()
 *以上是初始化编码器上下文过程
 *  6.检测数据帧(AVFrame)是否准备就绪 av_frame_make_writable(pFrame)
 *  7.往编码器发送数据帧(AVFrame) avcodec_send_frame(pCodecCtx, pFrame)
 *  8.等待接受编码好的数据包(AVPacket)做另外处理 avcodec_receive_packet(pCodecCtx, pPacket)
 *以上是使用编码器编码音视频操作
 *  9.停止编码器，需要往编码器中传输空帧，冲刷编码器 avcodec_send_frame(pCodecCtx, nullptr)
 *  10.关闭编码器上下文 avcodec_close(pCodecCtx)
 *  11.释放编码器上下文 avcodec_free_context(ppCodecCtx)
 *以上是释放编码器上下文过程
 *
 */
#include "FFMpegEncoder.h"

#define LOG_TAG "FFMpegEncoder"

bool FFMpegEncoder::initEncoder(EncoderParam &encoderParam) {
  auto pCodec = findCodec(encoderParam);
  if (!pCodec) {
    LOG_ERROR("findCodec error.")
    return false;
  }

  _pCodecCtx = avcodec_alloc_context3(pCodec);
  if (_pCodecCtx == nullptr) {
    LOG_ERROR("avcodec_alloc_context3 error.")
    return false;
  }

  _pCodecCtx->codec_id = pCodec->id;
  _pCodecCtx->codec_type = pCodec->type;
  _pCodecCtx->codec_tag = 0;
  int32_t ret = initContextParam(_pCodecCtx, encoderParam);
  if (ret != SUCCESS) {
    releaseEncoder();
    LOG_ERROR("initContextParam error.")
    return false;
  }

  ret = avcodec_open2(_pCodecCtx, pCodec, &_pOptions);
  if (ret < SUCCESS) {
    releaseEncoder();
    LOG_ERROR("avcodec_open2 error. errCode:%d, errStr:%s", ret, av_err2str(ret))
    return false;
  }

  _pPacket = av_packet_alloc();
  _State = EncoderState_Init;
  return true;
}

void FFMpegEncoder::releaseEncoder() {
  if (_State == EncoderState_Init || _State == EncoderState_Encode) {
    startEncode(nullptr);
  }

  _State = EncoderState_Release;

  if (_pPacket) {
    av_packet_free(&_pPacket);
    _pPacket = nullptr;
  }

  if (_pCodecCtx) {
    avcodec_close(_pCodecCtx);
    avcodec_free_context(&_pCodecCtx);
    _pCodecCtx = nullptr;
  }

  _State = EncoderState_None;
}

bool FFMpegEncoder::startEncode(AVFrame *pFrame) {
  if (_State == EncoderState_None)
    return false;

  int32_t ret = SUCCESS;
  if (pFrame) {
    ret = av_frame_make_writable(pFrame);
    if (ret < 0) {
      LOG_ERROR("av_frame_make_writable error. errCode:%d, errStr:%s", ret,
                av_err2str(ret))
      return false;
    }
  }

  ret = avcodec_send_frame(_pCodecCtx, pFrame);
  while(ret >= SUCCESS) {
    _State = EncoderState_Encode;
    ret = avcodec_receive_packet(_pCodecCtx, _pPacket);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
      break;
    } else if (ret < SUCCESS) {
      LOG_ERROR("avcodec_receive_packet error. errCode:%d, errStr:%s", ret,
                av_err2str(ret))
      return false;
    }

    if (_pEncodeCallback) {
      _pEncodeCallback->onEncodeData(this, _pPacket);
    }

    av_packet_unref(_pPacket);
  }

  _State = EncoderState_Init;
  return true;
}
